package com.neo.tiny.filter;

import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.sql.SQLException;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: druid连接池输出sql日志
 * @Author: yqz
 * @CreateDate: 2023/4/29 2:28 PM
 */
@Slf4j
@Configuration
public class DruidSqlLogFilter extends FilterEventAdapter {
    private static final SQLUtils.FormatOption FORMAT_OPTION = new SQLUtils.FormatOption(false, false);

    @Override
    protected void statementExecuteBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteBatchBefore(StatementProxy statement) {
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteQueryBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {
        statement.setLastExecuteTimeNano();
    }

    @Override
    protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
        statement.setLastExecuteTimeNano();
    }

    @Override
    protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
        statement.setLastExecuteTimeNano();
    }

    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        statement.setLastExecuteTimeNano();
    }

    @Override
    public void statement_close(FilterChain chain, StatementProxy statement) throws SQLException {
        super.statement_close(chain, statement);
        if (log.isInfoEnabled()) {
            String sql = statement.getBatchSql();
            if (!StringUtils.isEmpty(sql)) {
                int parametersSize = statement.getParametersSize();
                List<Object> parameters = new ArrayList<>(parametersSize);

                for (int i = 0; i < parametersSize; ++i) {
                    parameters.add(getJdbcParameter(statement.getParameter(i)));
                }

                String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
                String formattedSql = SQLUtils.format(sql, DbType.of(dbType), parameters, FORMAT_OPTION);
                printSql(formattedSql, statement);
            }
        }
    }

    private static Object getJdbcParameter(JdbcParameter jdbcParam) {
        if (jdbcParam == null) {
            return null;
        } else {
            Object value = jdbcParam.getValue();
            return value instanceof TemporalAccessor ? value.toString() : value;
        }
    }


    private static void printSql(String sql, StatementProxy statement) {
        String sqlLogger = "\n\n======= Sql Logger ======================\n{}\n======= Sql Execute Time: {} =======\n";
        log.info(sqlLogger, sql.trim(), format(statement.getLastExecuteTimeNano()));
    }

    private static String format(long nanos) {
        if (nanos < 1L) {
            return "0ms";
        } else {
            double millis = (double) nanos / 1000000.0;
            return millis > 1000.0 ? String.format("%.3fs", millis / 1000.0) : String.format("%.3fms", millis);
        }
    }

}
